#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
营业执照识别性能优化模块
提供内存管理、并发控制、缓存机制等性能优化功能
"""

import gc
import threading
import time
import hashlib
import json
import logging
import weakref
from typing import Dict, Optional, Any, Tuple
from dataclasses import dataclass, asdict
from concurrent.futures import ThreadPoolExecutor, as_completed
from functools import wraps, lru_cache
import psutil
import os
from contextlib import contextmanager


@dataclass
class PerformanceMetrics:
    """性能指标数据结构"""
    request_count: int = 0
    total_processing_time: float = 0.0
    average_processing_time: float = 0.0
    memory_usage_mb: float = 0.0
    cache_hit_rate: float = 0.0
    concurrent_requests: int = 0
    max_concurrent_requests: int = 0
    error_count: int = 0
    
    def to_dict(self) -> Dict:
        """转换为字典格式"""
        return asdict(self)


class MemoryManager:
    """内存管理器 - 优化图像处理的内存使用"""
    
    def __init__(self, logger: Optional[logging.Logger] = None):
        self.logger = logger or logging.getLogger(__name__)
        self._memory_threshold_mb = 500  # 内存使用阈值（MB）
        self._gc_threshold = 100  # 垃圾回收阈值
        self._operation_count = 0
        
    def get_memory_usage(self) -> float:
        """获取当前内存使用量（MB）"""
        try:
            process = psutil.Process(os.getpid())
            return process.memory_info().rss / 1024 / 1024
        except Exception:
            return 0.0
    
    def check_memory_pressure(self) -> bool:
        """检查内存压力"""
        current_memory = self.get_memory_usage()
        return current_memory > self._memory_threshold_mb
    
    @contextmanager
    def memory_optimized_operation(self, operation_name: str = "unknown"):
        """内存优化的操作上下文管理器"""
        start_memory = self.get_memory_usage()
        start_time = time.time()
        
        try:
            # 如果内存压力大，先进行垃圾回收
            if self.check_memory_pressure():
                self.force_garbage_collection()
            
            yield
            
        finally:
            # 操作完成后的清理
            self._operation_count += 1
            
            # 定期进行垃圾回收
            if self._operation_count % self._gc_threshold == 0:
                self.force_garbage_collection()
            
            # 记录内存使用情况
            end_memory = self.get_memory_usage()
            processing_time = time.time() - start_time
            
            self.logger.debug(
                f"内存优化操作完成: {operation_name}, "
                f"内存变化: {start_memory:.1f}MB -> {end_memory:.1f}MB, "
                f"耗时: {processing_time:.2f}s"
            )
    
    def force_garbage_collection(self):
        """强制垃圾回收"""
        collected = gc.collect()
        current_memory = self.get_memory_usage()
        self.logger.debug(f"垃圾回收完成: 回收对象 {collected} 个, 当前内存: {current_memory:.1f}MB")
    
    def optimize_image_array(self, image_array):
        """优化图像数组的内存使用"""
        if image_array is not None:
            # 确保使用合适的数据类型
            if image_array.dtype != 'uint8':
                image_array = image_array.astype('uint8')
            
            # 确保数组是连续的，提高访问效率
            if not image_array.flags['C_CONTIGUOUS']:
                image_array = image_array.copy()
        
        return image_array


class ConcurrencyManager:
    """并发管理器 - 管理并发请求和资源竞争"""
    
    def __init__(self, max_concurrent_requests: int = 5, logger: Optional[logging.Logger] = None):
        self.max_concurrent_requests = max_concurrent_requests
        self.logger = logger or logging.getLogger(__name__)
        
        # 使用信号量控制并发数量
        self._semaphore = threading.Semaphore(max_concurrent_requests)
        self._active_requests = 0
        self._max_active_requests = 0
        self._lock = threading.Lock()
        
        # 线程池用于并行处理
        self._thread_pool = ThreadPoolExecutor(
            max_workers=max_concurrent_requests,
            thread_name_prefix="BusinessLicense"
        )
    
    @contextmanager
    def concurrent_request(self, request_id: str = "unknown"):
        """并发请求上下文管理器"""
        acquired = False
        try:
            # 获取信号量
            acquired = self._semaphore.acquire(timeout=30)  # 30秒超时
            if not acquired:
                raise RuntimeError("系统繁忙，请稍后重试")
            
            # 更新活跃请求计数
            with self._lock:
                self._active_requests += 1
                self._max_active_requests = max(self._max_active_requests, self._active_requests)
            
            self.logger.debug(f"并发请求开始: {request_id}, 当前活跃请求: {self._active_requests}")
            
            yield
            
        finally:
            if acquired:
                # 释放信号量
                self._semaphore.release()
                
                # 更新活跃请求计数
                with self._lock:
                    self._active_requests -= 1
                
                self.logger.debug(f"并发请求结束: {request_id}, 当前活跃请求: {self._active_requests}")
    
    def get_concurrency_stats(self) -> Dict:
        """获取并发统计信息"""
        with self._lock:
            return {
                'active_requests': self._active_requests,
                'max_active_requests': self._max_active_requests,
                'max_concurrent_requests': self.max_concurrent_requests,
                'available_slots': self.max_concurrent_requests - self._active_requests
            }
    
    def submit_task(self, func, *args, **kwargs):
        """提交任务到线程池"""
        return self._thread_pool.submit(func, *args, **kwargs)
    
    def shutdown(self):
        """关闭线程池"""
        self._thread_pool.shutdown(wait=True)


class ResultCache:
    """结果缓存 - 缓存识别结果以提高性能"""
    
    def __init__(self, max_size: int = 100, ttl_seconds: int = 3600, logger: Optional[logging.Logger] = None):
        self.max_size = max_size
        self.ttl_seconds = ttl_seconds
        self.logger = logger or logging.getLogger(__name__)
        
        self._cache: Dict[str, Tuple[Any, float]] = {}  # key -> (value, timestamp)
        self._access_times: Dict[str, float] = {}  # key -> last_access_time
        self._lock = threading.RLock()
        
        self._hit_count = 0
        self._miss_count = 0
    
    def _generate_cache_key(self, image_path: str, image_size: int, image_mtime: float) -> str:
        """生成缓存键"""
        # 使用文件路径、大小和修改时间生成唯一键
        key_data = f"{image_path}:{image_size}:{image_mtime}"
        return hashlib.md5(key_data.encode()).hexdigest()
    
    def _is_expired(self, timestamp: float) -> bool:
        """检查缓存项是否过期"""
        return time.time() - timestamp > self.ttl_seconds
    
    def _cleanup_expired(self):
        """清理过期的缓存项"""
        current_time = time.time()
        expired_keys = []
        
        for key, (_, timestamp) in self._cache.items():
            if current_time - timestamp > self.ttl_seconds:
                expired_keys.append(key)
        
        for key in expired_keys:
            del self._cache[key]
            if key in self._access_times:
                del self._access_times[key]
        
        if expired_keys:
            self.logger.debug(f"清理过期缓存项: {len(expired_keys)} 个")
    
    def _evict_lru(self):
        """使用LRU策略驱逐缓存项"""
        if len(self._cache) <= self.max_size:
            return
        
        # 找到最久未访问的键
        lru_key = min(self._access_times.keys(), key=lambda k: self._access_times[k])
        
        # 删除LRU项
        del self._cache[lru_key]
        del self._access_times[lru_key]
        
        self.logger.debug(f"LRU驱逐缓存项: {lru_key}")
    
    def get(self, image_path: str) -> Optional[Any]:
        """获取缓存的识别结果"""
        try:
            # 获取文件信息
            if not os.path.exists(image_path):
                return None
            
            stat = os.stat(image_path)
            cache_key = self._generate_cache_key(image_path, stat.st_size, stat.st_mtime)
            
            with self._lock:
                if cache_key in self._cache:
                    value, timestamp = self._cache[cache_key]
                    
                    # 检查是否过期
                    if self._is_expired(timestamp):
                        del self._cache[cache_key]
                        if cache_key in self._access_times:
                            del self._access_times[cache_key]
                        self._miss_count += 1
                        return None
                    
                    # 更新访问时间
                    self._access_times[cache_key] = time.time()
                    self._hit_count += 1
                    
                    self.logger.debug(f"缓存命中: {cache_key}")
                    return value
                else:
                    self._miss_count += 1
                    return None
                    
        except Exception as e:
            self.logger.warning(f"获取缓存时发生错误: {str(e)}")
            self._miss_count += 1
            return None
    
    def put(self, image_path: str, result: Any):
        """存储识别结果到缓存"""
        try:
            # 获取文件信息
            if not os.path.exists(image_path):
                return
            
            stat = os.stat(image_path)
            cache_key = self._generate_cache_key(image_path, stat.st_size, stat.st_mtime)
            
            with self._lock:
                # 清理过期项
                self._cleanup_expired()
                
                # LRU驱逐
                self._evict_lru()
                
                # 存储新项
                current_time = time.time()
                self._cache[cache_key] = (result, current_time)
                self._access_times[cache_key] = current_time
                
                self.logger.debug(f"缓存存储: {cache_key}, 当前缓存大小: {len(self._cache)}")
                
        except Exception as e:
            self.logger.warning(f"存储缓存时发生错误: {str(e)}")
    
    def get_cache_stats(self) -> Dict:
        """获取缓存统计信息"""
        with self._lock:
            total_requests = self._hit_count + self._miss_count
            hit_rate = self._hit_count / total_requests if total_requests > 0 else 0.0
            
            return {
                'cache_size': len(self._cache),
                'max_size': self.max_size,
                'hit_count': self._hit_count,
                'miss_count': self._miss_count,
                'hit_rate': hit_rate,
                'ttl_seconds': self.ttl_seconds
            }
    
    def clear(self):
        """清空缓存"""
        with self._lock:
            self._cache.clear()
            self._access_times.clear()
            self._hit_count = 0
            self._miss_count = 0
            self.logger.info("缓存已清空")


class PerformanceMonitor:
    """性能监控器 - 收集和分析性能指标"""
    
    def __init__(self, logger: Optional[logging.Logger] = None):
        self.logger = logger or logging.getLogger(__name__)
        self._metrics = PerformanceMetrics()
        self._lock = threading.Lock()
        self._start_time = time.time()
    
    @contextmanager
    def measure_request(self, request_id: str = "unknown"):
        """测量请求性能的上下文管理器"""
        start_time = time.time()
        start_memory = self._get_memory_usage()
        
        try:
            with self._lock:
                self._metrics.concurrent_requests += 1
                self._metrics.max_concurrent_requests = max(
                    self._metrics.max_concurrent_requests,
                    self._metrics.concurrent_requests
                )
            
            yield
            
            # 请求成功完成
            processing_time = time.time() - start_time
            
            with self._lock:
                self._metrics.request_count += 1
                self._metrics.total_processing_time += processing_time
                self._metrics.average_processing_time = (
                    self._metrics.total_processing_time / self._metrics.request_count
                )
                self._metrics.memory_usage_mb = self._get_memory_usage()
            
            self.logger.debug(f"请求性能: {request_id}, 耗时: {processing_time:.2f}s")
            
        except Exception as e:
            # 请求失败
            with self._lock:
                self._metrics.error_count += 1
            raise
            
        finally:
            with self._lock:
                self._metrics.concurrent_requests -= 1
    
    def _get_memory_usage(self) -> float:
        """获取内存使用量（MB）"""
        try:
            process = psutil.Process(os.getpid())
            return process.memory_info().rss / 1024 / 1024
        except Exception:
            return 0.0
    
    def update_cache_hit_rate(self, hit_rate: float):
        """更新缓存命中率"""
        with self._lock:
            self._metrics.cache_hit_rate = hit_rate
    
    def get_metrics(self) -> PerformanceMetrics:
        """获取性能指标"""
        with self._lock:
            # 更新当前内存使用量
            self._metrics.memory_usage_mb = self._get_memory_usage()
            return PerformanceMetrics(**asdict(self._metrics))
    
    def reset_metrics(self):
        """重置性能指标"""
        with self._lock:
            self._metrics = PerformanceMetrics()
            self._start_time = time.time()
            self.logger.info("性能指标已重置")


# 全局性能管理器实例
memory_manager = MemoryManager()
concurrency_manager = ConcurrencyManager(max_concurrent_requests=5)
result_cache = ResultCache(max_size=100, ttl_seconds=3600)
performance_monitor = PerformanceMonitor()


def performance_optimized(cache_enabled: bool = True):
    """性能优化装饰器"""
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            # 生成请求ID
            request_id = f"{func.__name__}_{int(time.time() * 1000)}"
            
            # 并发控制
            with concurrency_manager.concurrent_request(request_id):
                # 性能监控
                with performance_monitor.measure_request(request_id):
                    # 内存优化
                    with memory_manager.memory_optimized_operation(func.__name__):
                        # 缓存检查（如果启用且是识别函数）
                        if cache_enabled and len(args) > 1 and isinstance(args[1], str):
                            image_path = args[1]
                            cached_result = result_cache.get(image_path)
                            if cached_result is not None:
                                return cached_result
                        
                        # 执行原函数
                        result = func(*args, **kwargs)
                        
                        # 缓存结果（如果启用且是识别函数）
                        if cache_enabled and len(args) > 1 and isinstance(args[1], str):
                            image_path = args[1]
                            result_cache.put(image_path, result)
                        
                        return result
        
        return wrapper
    return decorator


def get_performance_stats() -> Dict:
    """获取综合性能统计信息"""
    metrics = performance_monitor.get_metrics()
    cache_stats = result_cache.get_cache_stats()
    concurrency_stats = concurrency_manager.get_concurrency_stats()
    
    # 更新缓存命中率到性能监控器
    performance_monitor.update_cache_hit_rate(cache_stats['hit_rate'])
    
    return {
        'performance_metrics': metrics.to_dict(),
        'cache_stats': cache_stats,
        'concurrency_stats': concurrency_stats,
        'memory_usage_mb': memory_manager.get_memory_usage(),
        'uptime_seconds': time.time() - performance_monitor._start_time
    }